package de.koller.aframe.gl;

import de.koller.oceanFramework.OceanException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import static org.lwjgl.opengl.ARBBufferObject.GL_DYNAMIC_DRAW_ARB;
import static org.lwjgl.opengl.ARBBufferObject.GL_STATIC_DRAW_ARB;

import static org.lwjgl.opengl.ARBBufferObject.glBindBufferARB;
import static org.lwjgl.opengl.ARBBufferObject.glBufferDataARB;
import static org.lwjgl.opengl.ARBBufferObject.glDeleteBuffersARB;
import static org.lwjgl.opengl.ARBBufferObject.glGenBuffersARB;
import static org.lwjgl.opengl.ARBBufferObject.glMapBufferARB;
import static org.lwjgl.opengl.ARBBufferObject.glUnmapBufferARB;
import static org.lwjgl.opengl.ARBVertexBufferObject.*;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GLContext;

public class AVertexBuffer {
	
	private int vboID = -1;
	public final UsageType usageType;
	public final int primitivType;
	private FloatBuffer data = null;
	private ByteBuffer nativBuffer = null;
	
	private int normal = 0;
	private int color = 0;
	private int vertex = 0;
	private int[] textures = null;
	private int stride = -1;
	
	private int dataCount = -1;
	
	public AVertexBuffer( UsageType usageTye, int primitivType ) {
		this.usageType = usageTye;
		this.primitivType = primitivType;
	}
	
	public static AVertexBuffer create( UsageType u, int primitiveType ) {
		AVertexBuffer a = new AVertexBuffer( u, primitiveType );
		return a;
	}
	
	public static AVertexBuffer create() {
		return new AVertexBuffer( UsageType.Static, GL11.GL_TRIANGLES );
	}
	
	private void startUse() {
		if( !GLContext.getCapabilities().GL_ARB_vertex_buffer_object ) 
			throw new OceanException( "VBOs are not supported!" );
		
		createID();
	}

	public void changeData( int offset, float... d ) {
		if( usageType != UsageType.Dynamic )
			throw new OceanException( "can't edit vbo!" );
		
		data.position( offset );
		data.put( d );
	}
	
	private void createID() {
		vboID = glGenBuffersARB();
	}

	public void setData( float... d ) {
		if( data != null )
			throw new OceanException( "Data already created!" );
		
		bind();
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, d.length*4, usageType.usage );
		nativBuffer = glMapBufferARB( GL_ARRAY_BUFFER_ARB, GL15.GL_WRITE_ONLY, null );
		data = nativBuffer.asFloatBuffer();
		data.put( d );
		
		dataCount = d.length;
		glUnmapBufferARB( GL_ARRAY_BUFFER_ARB );
	}

	public void bind() {
		if( vboID == -1 )
			startUse();
		
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboID );
	}

	/** parameter geben an, wieviel floats pro typ verwendet werden, textue(multitexture) 
	  im array in dieser reihenfolge. */
	public void setup( int vertex, int color, int normal, int... texture ) {
		this.normal = normal;
		this.color = color;
		this.vertex = vertex;
		this.textures = texture;
		
		int t = 0;
		for( int i = 0; i < texture.length; i++ )
			t += texture[i];
		
		this.stride = (normal + color + vertex + t) * 4;
	}
	
	public void renderThis() {
		if( stride == -1 )
			throw new OceanException( "setup not yet called!" );
		
		bind();
		
		if( vertex <= 0 )
			throw new OceanException( "Vertex needed!" );
		
		GL11.glEnableClientState( GL11.GL_VERTEX_ARRAY );
		GL11.glVertexPointer( vertex, GL11.GL_FLOAT, stride, 0 );
		
		if( color > 0 ) {
			GL11.glEnableClientState( GL11.GL_COLOR_ARRAY );
			GL11.glColorPointer( color, GL11.GL_FLOAT, stride, vertex*4 );
		}
		
		if( normal > 0 ) {
			GL11.glEnableClientState( GL11.GL_NORMAL_ARRAY );
			GL11.glNormalPointer( normal, stride, (vertex+color)*4 );
		}
		
		if( textures.length >= 1 && textures[0] > 0 ) {
			GL11.glEnableClientState( GL11.GL_TEXTURE_COORD_ARRAY );
			GL11.glTexCoordPointer( textures[0], GL11.GL_FLOAT, stride, (vertex+color+normal)*4 );
		}
		
		if( textures.length > 1 )
			throw new UnsupportedOperationException( "multitexturing not yet supported" );

		GL11.glDrawArrays( primitivType, 0, dataCount / (stride/4) );
		
		GL11.glDisableClientState( GL11.GL_COLOR_ARRAY );
		GL11.glDisableClientState( GL11.GL_NORMAL_ARRAY );
		GL11.glDisableClientState( GL11.GL_TEXTURE_COORD_ARRAY );
		GL11.glDisableClientState( GL11.GL_VERTEX_ARRAY );
	}
	
	public void destroy() {
		glDeleteBuffersARB( vboID );
	}
	
	public enum UsageType {
		Static( GL_STATIC_DRAW_ARB ),
		Dynamic( GL_DYNAMIC_DRAW_ARB );
		
		private final int usage;

		private UsageType( int usage ) {
			this.usage = usage;
		}
	}
	
}
